﻿using System;
using System.Collections.ObjectModel;
using System.IdentityModel.Tokens;
using SecurityTokenServiceNS;
using System.IdentityModel.Claims;
using System.ServiceModel.Security;
using System.ServiceModel.Security.Tokens;
using Microsoft.Interop.Security.AzRoles;
using System.ServiceModel;
using System.Collections.Generic;

namespace IssuedClaimsAddIns
{
	public class IssuedClaimsModuleAzMan : IIssuedClaimsProcessor
	{
		private IAzApplication _azApp;

		public IssuedClaimsModuleAzMan()
		{
			AzAuthorizationStore store = new AzAuthorizationStoreClass();
			store.Initialize(0, "msxml://" + StsConfiguration.Current.AzManCollection.GetValueByKey("path"), null);
			_azApp = store.OpenApplication(StsConfiguration.Current.AzManCollection.GetValueByKey("appName"), null);
		}

		public void GetIssuedClaims(RST rst, Collection<Claim> claims)
		{
			List<string> claimOps = GetOperations(rst.Claims);
			if (claimOps.Count == 0)
				return;

			if (AccessGranted(claimOps, rst.AppliesTo.Uri.ToString()))
				claims.Add(new Claim(ClaimTypes.AuthorizationDecision, "ok", Rights.PossessProperty));
			else 
				throw new FaultException("AzMan: Access was denied!");
		}

		private bool AccessGranted(List<string> claimOps, string scope)
		{
			Dictionary<string, int> opToId = new Dictionary<string, int>(_azApp.Operations.Count);
			object[] operations = new object[claimOps.Count];

			foreach (IAzOperation op in _azApp.Operations)
			    opToId.Add(op.Name, op.OperationID);

			int i = 0, opId;
			foreach (string claimOp in claimOps)
			{
				if (opToId.TryGetValue(claimOp, out opId))
					operations[i] = (object)opId;
				else
					return false;
			}

			IAzClientContext ctx = _azApp.InitializeClientContextFromStringSid(GetSID(), 0, null);

			object[] scopes = { scope };
			object[] results = (object[])ctx.AccessCheck("sts", scopes, operations, null, null, null, null, null);
			return (int)results[0] == 0;
		}

		private List<string> GetOperations(IList<ClaimTypeRequirement> claims)
		{
			List<string> operations = new List<string>();
			foreach (ClaimTypeRequirement claim in claims)
				if (claim.ClaimType.StartsWith(Constants.WSIdentity.ClaimTypes.AuthDecision, StringComparison.CurrentCulture))
					operations.Add(claim.ClaimType.Substring(Constants.WSIdentity.ClaimTypes.AuthDecision.Length+1));
			return operations;
		}

		private string GetSID()
		{
			ReadOnlyCollection<ClaimSet> claimSets = OperationContext.Current.ServiceSecurityContext.AuthorizationContext.ClaimSets;
			foreach (ClaimSet claimSet in claimSets)
				foreach (Claim claim in claimSet)
					if (claim.ClaimType == ClaimTypes.Sid)
						return claim.Resource.ToString();
			return String.Empty;
		}
	}
}
